﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Diagnostics;
using System.Dynamic;
using System.Collections;
using System.Runtime.Serialization;
using System.Linq.Expressions;
using System.Text.RegularExpressions;

namespace TurboTemplate
{

    [DataContract(IsReference=true)]
    public class ListWithName : IEnumerable<ListWithName> // : ICollection<string>
    {

        //private List<string> m_List;
        
        [DataMember()]
        public TransformInstructions Instructions { get; set; }

        //[DataMember()]
        //public List<string> ListOfStrings
        //{
        //    get { return m_List; }
        //    set { m_List = value; }
        //}

        [DataMember()] 
        public string Name { get; set; }

        [DataMember()]
        public List<ListWithName> ChildLists { get; set; }

        [DataMember()]
        public ListWithName Parent { get; set; }

        public ListWithName()
        {
            //m_List = new List<string>();
            ChildLists = new List<ListWithName>();
        }

        public ListWithName(string name) : 
            this()
        {
            this.Name = name;
        }

        public ListWithName(string name, params string[] items)
            : this(name)
        {
            //m_List.AddRange(items);
        }

        public ListWithName ItemByName(string name)
        {
            return this[name];
        }

        public ListWithName this[int index]
        {
            get
            {
                return ChildLists.ElementAtOrDefault(index);
            }
        }

        public ListWithName this[string name]
        {
            get
            {
                return ChildLists.SingleOrDefault(x => x.Name == name);
            }
        }

        public ListWithName AddChildList(string name, params string[] items)
        {
            var childList = new ListWithName(name, items);
            this.ChildLists.Add(childList);
            return childList;
        }

        public override string ToString()
        {
            return this.Name;
        }
        //#region ICollection

        //public void Add(string item)
        //{
        //    m_List.Add(item);
        //}

        //public void Clear()
        //{
        //    m_List.Clear();
        //}

        //public bool Contains(string item)
        //{
        //    return m_List.Contains(item);
        //}

        //public void CopyTo(string[] array, int arrayIndex)
        //{
        //    m_List.CopyTo(array, arrayIndex);
        //}

        //public int Count
        //{
        //    get { return m_List.Count; }
        //}

        //public bool IsReadOnly
        //{
        //    get { return false; }
        //}

        //public bool Remove(string item)
        //{
        //    return m_List.Remove(item);
        //}

        //public IEnumerator<string> GetEnumerator()
        //{
        //    return m_List.GetEnumerator();
        //}

        //IEnumerator IEnumerable.GetEnumerator()
        //{
        //    return m_List.GetEnumerator();
        //}

        //#endregion

        public IEnumerator<ListWithName> GetEnumerator()
        {
            return ChildLists.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return ChildLists.GetEnumerator();
        }

    }

    [DataContract(IsReference = true)]
    public class ListWithNameAndDepth
    {
        [DataMember()]
        public ListWithName List { get; set; }
        [DataMember()]
        public int Depth { get; set; }

        public override string ToString()
        {
            //Regex.Replace("m", "@()", "$1");
            return List.Name;
        }
    }

	[DataContract(IsReference=true), KnownType(typeof(DynamicDictionary)), KnownType(typeof(ListWithName))]
	public class TransformInstructions
	{
		[DataMember()]
		public string SourcedFromConnection { get; set; } // Optional when we have lists
		[DataMember()]
		public SchemaStoredProcedure ProcedureFromSchema { get; set; } // Optional when we have lists
		[DataMember()]
		public SchemaTable TableFromSchema { get; set; } // Optional when we have lists

		[DataMember()]
		public TransformTemplate Template { get; set; } // Not optional

		[DataMember()]
		public TransformModel ModelForTransform { get; private set; }
		[DataMember()]
		public string TransformedText { get; set; }
		[DataMember()]
		public string UserEnteredEntityName { get; set; }

        [DataMember()]
        public ListWithName ListSource { get; set; }

		public TransformInstructions()
		{
            ListSource = new ListWithName();
            ListSource.Instructions = this;
			ModelForTransform = new TransformModel();
			ModelForTransform.Instructions = this;
		}
	}

	[DataContract(IsReference=true), KnownType(typeof(DynamicDictionary))]
	public class TransformModel
	{
		[DataMember()]
		public DynamicDictionary ViewBag { get; set; }
		[DataMember()]
		public TransformInstructions Instructions { get; set; }
		[DataMember()]
		public DataSet Data { get; set; }

		public void TransformComplete()
		{
            if (!string.IsNullOrWhiteSpace(Instructions.UserEnteredEntityName))
			{
                ViewBag.SetValue("EntityName", Instructions.UserEnteredEntityName);
			}
			else
			{
                if (Instructions.ProcedureFromSchema != null)
                {
                    ViewBag.SetValue("EntityName", (Instructions != null && Instructions.ProcedureFromSchema != null ? Instructions.ProcedureFromSchema.SPECIFIC_NAME : Instructions.TableFromSchema.TABLE_NAME));
                }
                else if (Instructions.ListSource != null)
                {
                    ViewBag.SetValue("EntityName", Instructions.ListSource.Name);   
                }
			}
		}

		public string EnumerateParameters(string prefix = "", string suffix = "")
		{
			if (Instructions == null || Instructions.ProcedureFromSchema == null || Instructions.ProcedureFromSchema.Parameters == null)
				return "";

			StringBuilder ret = new StringBuilder();
			string prefixOriginal = prefix;
			string suffixOriginal = suffix;

			foreach (var p in Instructions.ProcedureFromSchema.Parameters)
			{
				InlineReplacements(p.ParameterName, p.DataType.ToString(), prefixOriginal, suffixOriginal, ref prefix, ref suffix);
				ret.AppendLine(string.Format("{0}{1}{2}", prefix, p.ParameterName.Replace("@", ""), suffix));
            } 
            if (ret.Length > 1)
                ret = ret.Remove(ret.Length - 2, 2); // Trailing newline
			return ret.ToString();
		}

		public string EnumerateColumnNames(int index, string prefix = "", string suffix = "")
		{
			if (Data == null)
				return "";

			StringBuilder ret = new StringBuilder();
			string prefixOriginal = prefix;
			string suffixOriginal = suffix;

			foreach (DataColumn c in Data.Tables[index].Columns)
			{
				InlineReplacements(c.ColumnName, c.DataType.ToString(), prefixOriginal, suffixOriginal, ref prefix, ref suffix);
				ret.AppendLine(string.Format("{0}{1}{2}", prefix, c.ColumnName, suffix));
			}
			if (ret.Length > 1)
				ret = ret.Remove(ret.Length - 2, 2); // Trailing newline
			return ret.ToString();
		}

		private void InlineReplacements(
			string itemName, 
			string dataType,
			string prefixOriginal, 
			string suffixOriginal, 
			ref string prefix, 
			ref string suffix)
		{
			InlineReplacements(itemName, dataType, prefixOriginal, ref prefix);
			InlineReplacements(itemName, dataType, suffixOriginal, ref suffix);
		}

		private void InlineReplacements(
			string itemName,
			string dataType,
			string original,
			ref string current)
		{
			// Expansions first
            current = original.Replace("#NAME", itemName);
            current = current.Replace("#DATATYPE", dataType);
			current = current.Replace("#GETFIELD", ResolveGetterFromDataType(dataType));

			// Now do a replacement function which is R#(input, replacethis, withthis)
			System.Text.RegularExpressions.Regex r = new System.Text.RegularExpressions.Regex(@"#R\((?<INPUT>[^,]+?),(?<REPLACETHIS>[^,]+?),(?<WITHTHIS>.*)??\)");
			bool checkReplacement = (current.IndexOf("#R") > -1);
			while(checkReplacement)
			{
				var m = r.Match(current);
				if(!m.Success)
				{
					checkReplacement = false;
					break;
				}
				string replacement = m.Groups["INPUT"].Value.Replace(m.Groups["REPLACETHIS"].Value, m.Groups["WITHTHIS"].Value);
				current = current.Remove(m.Index, m.Length);
				current = current.Insert(m.Index, replacement);
			}
		}

        public string Replace(string source, string oldString, string newString)
        {
            return source.Replace(oldString, newString);
        }

        public List<ListWithNameAndDepth> FlattenList()
        {
            var ret = new List<ListWithNameAndDepth>();
            var ls = new Stack<Tuple<int, ListWithName>>(this.Instructions.ListSource.Select(x => new Tuple<int, ListWithName>(0, x)));

            while (ls.Count > 0)
            {
                var list = ls.Pop();
                ret.Add(new ListWithNameAndDepth() { List = list.Item2, Depth = list.Item1 });
                foreach (var l in list.Item2.ChildLists)
                    ls.Push(new Tuple<int, ListWithName>(list.Item1 + 1, l));
            }

            return ret;
        }

		public string ResolveGetterFromDataType(string t)
		{
            if (t == typeof(string).ToString() || t == DbType.String.ToString())
				return "String";
			else if (t == typeof(int).ToString() || t == DbType.Int32.ToString())
				return "Int32";
			else if (t == typeof(bool).ToString() || t == DbType.Boolean.ToString())
				return "Boolean";
			else if (t == typeof(long).ToString() || t == DbType.Int64.ToString())
				return "Int64";
			else if (t == typeof(DateTime).ToString() || t == DbType.DateTime.ToString() || t == DbType.DateTime2.ToString() || t == DbType.Date.ToString())
				return "DateTime";
			else if (t == typeof(double).ToString() || t == DbType.Double.ToString())
				return "Double";
			else if (t == typeof(Decimal).ToString() || t == DbType.Decimal.ToString())
				return "Decimal";
			else if (t == typeof(byte).ToString() || t == DbType.Binary.ToString() || t == DbType.Byte.ToString())
				return "Byte";
			else
				return "Item";
		}

		public TransformModel()
		{
			ViewBag = new DynamicDictionary();
		}

	}
}
